﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Reactive.Linq;

namespace ReactiveGameDemo
{
    class Player : GameObject
    {
        public int ammoCount;

        public Player(Game game) : base(game, "player") { }

        public override void Initialize()
        {
            base.Initialize();
            ammoCount = 3;
            position = new Vector2(GraphicsDevice.Viewport.Width,
                                   GraphicsDevice.Viewport.Height) / 2;
            rotation = MathHelper.PiOver2;

            // simple filter (shooting)
            IObservable<Keys> whenToShoot = keyPresses.Where( key => key == Keys.Enter && ammoCount > 0 );
            subscriptions.Add( whenToShoot.Subscribe( shootKey => shoot() ) );

            // simple projection (bad steering)
            IObservable<float> whenToSteer = keyPresses
                .Where( key => key == Keys.Left || key == Keys.Right )
                .Select( key => key == Keys.Left ? -0.05f : +0.05f );
            subscriptions.Add( whenToSteer.Subscribe(steer) );

            // more complex projection (better steering and thrust)
            IObservable<Keys> keyHolds = keyPresses.SelectMany( keyDown => 
                Observable.Interval( TimeSpan.FromMilliseconds(15) )
                    .Select( tick => keyDown )
                    .TakeUntil( keyReleases.Where( keyUp => keyUp == keyDown ) ) );

            var whenToSteerContinuous = from key in keyHolds
                                        where key == Keys.Left || key == Keys.Right
                                        select key==Keys.Left ? -0.05f : +0.05f ;
            subscriptions.Add( whenToSteerContinuous.Subscribe(steer) );

            IObservable<Keys> whenToThrust = keyHolds.Where( key => key == Keys.Up );
            subscriptions.Add( whenToThrust.Subscribe( thrustKey => thrust() ) );

            // serial filter combination (combo attack input sequence)
            IObservable<Keys> whenToFireCombo = keyPresses.Where(key => key == Keys.Up).SelectMany(
                keyPresses.Take(1).Where(key => key == Keys.Down).SelectMany(
                keyPresses.Take(1).Where(key => key == Keys.Space)));
            subscriptions.Add( whenToFireCombo.Subscribe( combo => fireCombo() ) );

            // concurrent filter combination (ammo pickup)
            IObservable<GameObject> whenToPickup = Observable.Join(
                collisionEnter.Where(ammo => ammo is Ammo),
                keyPresses.Where(key => key == Keys.Space),
                ammoEnter => collisionExit.Where(ammoExit => ammoExit == ammoEnter),
                keyDown => keyReleases.Where(keyUp => keyUp == keyDown),
                (ammo, key) => ammo);
            subscriptions.Add( whenToPickup.Subscribe(pickupAmmo) );

            //subscriptions.Dispose();
        }

        public void shoot()
        {
            Bullet newBullet = new Bullet(Game);
            newBullet.position = position;
            newBullet.rotation = rotation;
            ammoCount--;
        }

        public void steer(float angle)
        {
            rotation += angle;
        }

        public void thrust()
        {
            position += 3 * Forward;
        }

        public void fireCombo()
        {
            int shots = 5;
            float angle = MathHelper.Pi/4;
            for (int i = 0; i < shots; i++)
            {
                Bullet newBullet = new Bullet(Game);
                newBullet.position = position + Forward * size;
                newBullet.rotation = rotation - angle / 2 + i * angle / (shots - 1);
            }
            ammoCount--;
        }

        public void pickupAmmo(GameObject other)
        {
            if (Game.Components.Contains(other))
            {
                Game.Components.Remove(other);
                ammoCount += 3;
                new Ammo(Game);
            }
        }

    }
}
